#pragma once

#include <iostream>
#include "DataHolder.h"
#include <vector>
#include <cassert>

using namespace std;

class Memory;
class Data;

class Expression : public DataHolder {
public:
	
	enum Operation {
		SINGLE_VALUE,
		ADD,
		MULTIPLY
	};
	
	~Expression();
	
	static Expression* createExpression(Type resultType, Memory* ownerMemory, Operation operation = ADD);
	
	static Expression* createSingleBoolean(bool value, Memory* ownerMemory);
	static Expression* createSingleInt(int value, Memory* ownerMemory);
	static Expression* createSingleFloat(float value, Memory* ownerMemory);
	static Expression* createSingleString(string value, Memory* ownerMemory);
	static Expression* createSingleVariableName(string value, Memory* ownerMemory);	
	
	void addSubExpression(DataHolder* subExpression);
	
	Type getType();
	
	bool getBooleanValue();
	int getIntValue();
	float getFloatValue();
	string getStringValue();
    string getVariableName();
	
	static int getNrOfInstances() { return s_nrOfInstances; }
	
	void setReferenced(bool value);
	
private:
	
	typedef vector<DataHolder*> DataHolders;
	DataHolders m_subExpressions;
	
	Operation m_operation;
	Memory* m_ownerMemory;
	
	static int s_nrOfInstances;
	
	
	Expression(Type resultType, Memory* ownerMemory, Operation operation);
	Data* getDataFromMemoryUsingVariableName(const string& varName);
	
	// Template function for performing operations on a list of values
	template<class T>
	T resolve(vector<T> theVector) {
		T result = 0;
		
		if (m_operation == SINGLE_VALUE) {
			assert(theVector.size() == 1);
			result = theVector[0];
		}
		else if (m_operation == ADD) {
			for (int i = 0; i < theVector.size(); ++i) {
				result += theVector[i];
			}
			return result;
		}
		else if (m_operation == MULTIPLY) {
			if (theVector.size() == 0) {
				return 0;
			} else if (theVector.size() == 1) {
				return theVector[0];
			} else {
				result = theVector[0];
				for (int i = 1; i < theVector.size(); ++i) {
					result *= theVector[i];
				}
				return result;
			}
		}
		
		return result;
	}
};

